package com.hundsun.network.aop.filters;

import java.io.IOException;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.filter.GenericFilterBean;

import com.hundsun.jres.common.util.CollectionUtils;
import com.hundsun.jresplus.common.util.StringUtil;

public class CsrfFilter extends GenericFilterBean implements Filter {

	private static Log _log = LogFactory.getLog(CsrfFilter.class);

	/**
	 * 根域名（有可能配二级域名，所以需要解析出根域名）
	 */
	private String rootDomain;

	private String plantformLoginUrl;
	/**
	 * 排除的url地址
	 */
	private List<String> excludedReferer;

	@Value("${app.domain:}")
	private String appDomain;

	@Override
	public void afterPropertiesSet() throws ServletException {
		this.rootDomain = this.getRootDomain(appDomain);
	}

	@Override
	public void destroy() {

	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse res, FilterChain filterChain)
			throws IOException, ServletException {
		_log.debug("begin CSRFFilter ...");
		HttpServletRequest req = (HttpServletRequest) request;

		if (isCsrf(req)) {
			_log.info("find csrf attack, referer url is [" + req.getHeader("Referer") + "]");
			redirectUrl(res, getLoginUrl());
			return;
		}

		filterChain.doFilter(req, res);
	}

	/**
	 * 是否是csrf请求
	 * 
	 * @param request
	 * @return
	 * @author tanhl
	 */
	private boolean isCsrf(HttpServletRequest request) {
		/*
		 * String httpMethod = request.getMethod();
		 * if(!StringUtil.equalsIgnoreCase(httpMethod, "POST")) { return false;
		 * }
		 */
		if (StringUtil.isBlank(rootDomain)) {
			_log.error("错误:rootDomain 为空");
			return false;
		}
		String refererUrl = request.getHeader("Referer");
		if (StringUtil.isBlank(refererUrl)) {
			return false;
		}

		// 请求url的域名
		String reqDomain = this.parseDomainByUrl(refererUrl);
		if (StringUtil.isBlank(reqDomain)) {
			_log.error("url 解析域名失败:" + refererUrl);
			return false;
		}

		// add by caizj 特殊url不作跨域拦截 start
		if (!CollectionUtils.isEmpty(excludedReferer)) {
			if (excludedReferer.contains(reqDomain)) {
				_log.debug("url:" + refererUrl + "is an excluded url, CsrfFilter do nothing");
				return false;
			}
		}
		// end

		if (rootDomain.contains(reqDomain)) {
			return false;
		}
		return true;
	}

	private String getLoginUrl() {
		return this.plantformLoginUrl;
	}

	private String parseDomainByUrl(String url) {
		if (StringUtil.isBlank(url)) {
			_log.error("parseDomainByUrl: url is null");
			return null;
		}
		if (!url.endsWith("/")) {
			url = url + "/";
		}
		boolean isSSL = url.startsWith("https");
		if ((url.startsWith("http://localhost")) || (url.startsWith("https://localhost"))
		    || (url.startsWith("http://127.0.0.1")) || url.startsWith("https://127.0.0.1")
		    //排除私有网段地址
		    || (url.startsWith("http://10.")) || url.startsWith("https://10.")
		    || (url.startsWith("http://172.16.")) || url.startsWith("https://172.16.")
		    || (url.startsWith("http://192.168.")) || url.startsWith("https://192.168.")) {
			_log.warn("parseDomainByUrl: url is localhost, return null");
			return null;
		}
		int startIndex = url.indexOf(".");
		int slashIndex = -1;
		int colonIndex = -1;
		if (isSSL) {
			slashIndex = url.indexOf("/", "https://".length() + 1);
			colonIndex = url.indexOf(":", "https://".length() + 1);
		} else {
			slashIndex = url.indexOf("/", "http://".length() + 1);
			colonIndex = url.indexOf(":", "http://".length() + 1);
		}
		int endIndex = colonIndex > 0 ? colonIndex : slashIndex;
		String domain = url.substring(startIndex, endIndex);
		return domain;
	}

	private void redirectUrl(ServletResponse response, String url) throws IOException {
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		httpResponse.sendRedirect(url);
	}

	/**
	 * 获取根域名(参数必须是域名如：a.com,bb.com)
	 * 
	 * @return
	 * @author tanhl
	 */
	private String getRootDomain(String appDomain) {
		// 判断是否是ip，如果是ip，直接返回
		if (isValidIpAddress(appDomain)) {
			return appDomain;
		}
		String sysAppDomain = null;
		if (!appDomain.startsWith(".")) {
			sysAppDomain = "." + appDomain;
		} else {
			sysAppDomain = appDomain;
		}
		return sysAppDomain;
	}

	/**
	 * 是否有效的ip地址
	 * 
	 * @param ip
	 * @return
	 * @author tanhl
	 */
	private boolean isValidIpAddress(String ip) {
		if (StringUtil.isBlank(ip)) {
			return false;
		}
		ip = StringUtil.trim(ip);
		String[] numArr = ip.split("\\.");
		if (numArr == null || numArr.length != 4) {
			return false;
		}
		for (String num : numArr) {
			if (StringUtil.isBlank(num)) {
				return false;
			}
			if (!StringUtil.isNumeric(num)) {
				return false;
			}
			int numInt = Integer.valueOf(num);
			if (numInt < 0 || numInt > 255) {
				return false;
			}
		}
		return true;
	}

	public String getPlantformLoginUrl() {
		return plantformLoginUrl;
	}

	public void setPlantformLoginUrl(String plantformLoginUrl) {
		this.plantformLoginUrl = plantformLoginUrl;
	}

	public List<String> getExcludedReferer() {
		return excludedReferer;
	}

	public void setExcludedReferer(List<String> excludedReferer) {
		this.excludedReferer = excludedReferer;
	}
}
